using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;

namespace ReportMaintenanceUtility
{
	public class ReportManager
	{
		#region PRIVATE MEMBERS
		private ReportWrapper _activeReport = null;
		private ReportWrapper _template = null;
		private Dictionary<string, ReportWrapper> _reports = null;
		#endregion

		#region PUBLIC PROPERTIES
		public ReportWrapper ActiveReport
		{
			get { return this._activeReport; }
			set { this._activeReport = value; }
		}
		public ReportWrapper Template
		{
			get { return this._template; }
		}
		public Dictionary<string, ReportWrapper> Reports
		{
			get { return this._reports; }
		}
		#endregion

		#region CONSTRUCTOR(S)
		public ReportManager() : this(string.Empty) { }
		public ReportManager(string templateFilename)
		{
			this._reports = new Dictionary<string, ReportWrapper>();
			if (!string.IsNullOrEmpty(templateFilename) && File.Exists(templateFilename))
			{
				FileInfo fi = new FileInfo(templateFilename);
				this._template = new ReportWrapper(this.LoadTemplate(fi), fi);
			}
		}
		#endregion

		#region PUBLIC METHODS
		public void AddReport(FileInfo fi, Report report)
		{
			if (!this.Reports.ContainsKey(fi.Name))
			{
				this.Reports.Add(fi.Name, new ReportWrapper(report, fi));
			}
		}
		public void GenerateComparisonReport(string saveAs, DetailLevel dl)
		{
			if (this.Template != null)
			{
				StringBuilder sb = new StringBuilder();
				switch (dl)
				{
					case DetailLevel.ParameterConformityByReport:
						this.CreateParamterConformityReport(sb);
						break;
					case DetailLevel.AttributeConformityByReport:
						this.CreateAttributeConformityReport(sb);
						break;
					case DetailLevel.ParameterConformityAllReports:
						this.CreateParamterConformityAllReports(sb);
						break;
					case DetailLevel.AttributeConformityAllReports:
						this.CreateAttributeConformityAllReports(sb);
						break;
					case DetailLevel.DetailedParameterComparison:
						this.CreateDetailedParameterComparison(sb);
						break;
				}

				try
				{
					File.WriteAllText(saveAs, sb.ToString());
				}
				catch (IOException ex)
				{
					//File.Exists(string.Format("{0}{1}.txt", saveAs
					MessageBox.Show(string.Format("Unable to save report to file!\n{0}", ex.Message));
				}
				catch (Exception ex)
				{
					MessageBox.Show(string.Format("Unable to save report to file!\n{0}", ex.Message));
				}

			}
		}
		public static bool SaveChanges(ReportWrapper rw)
		{
			if (!rw.ReadOnly && rw.IsDirty)
			{
				try
				{
					XmlSerializer serializer = new XmlSerializer(typeof(Report));
					using (TextWriter tw = new StreamWriter(rw.Path, false, Encoding.UTF8))
					{
						serializer.Serialize(tw, rw.OriginalReport);
						tw.Flush();
						tw.Close();
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(string.Format("Unable to save changes to report {0}!\n{1}", rw.Name, ex.Message));
					return false;
				}
			}
			return true;
		}
		#endregion

		#region PRIVATE METHODS
		private Report LoadTemplate(FileInfo fi)
		{
			Report rpt = null;
			try
			{
				using (TextReader tr = fi.OpenText())
				{
					XmlSerializer serializer = new XmlSerializer(typeof(Report));
					rpt = (Report)serializer.Deserialize(tr);
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
			return rpt;
		}
		private Report LoadTemplate(string filename)
		{
			if (File.Exists(filename))
			{
				return this.LoadTemplate(new FileInfo(filename));
			}
			else if (File.Exists(string.Format("{0}\\{1}", Application.StartupPath, filename)))
			{
				return this.LoadTemplate(new FileInfo(string.Format("{0}\\{1}", Application.StartupPath, filename)));
			}
			return null;
		}
		private void CreateParamterConformityReport(StringBuilder sb)
		{
			bool pass = true;
			bool match = false;

			//create header row
			sb.Append(Constants.LABEL_REPORT_NAME);
			foreach (string s in this.ActiveReport.Parameters.Keys)
			{
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, s);
			}
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			sb.AppendLine(string.Empty);

			//compare all parameters to template
			sb.Append(this.ActiveReport.Name);
			foreach (Parameter p in this.ActiveReport.Parameters.Values)
			{
				//call each paramters Equal() method, passing in corresponding template parameter...
				if (this.Template.Parameters.ContainsKey(p.Name))
				{
					match = p.Equals(this.Template.Parameters[p.Name]);
					if (match)
					{
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_YES);
					}
					else
					{
						pass = false;
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NO);
					}
				}
				else
				{
					pass = false;
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NA);
				}
			}
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (pass ? Constants.LABEL_YES : Constants.LABEL_NO));
			sb.AppendLine(string.Empty);
		}
		private void CreateAttributeConformityReport(StringBuilder sb)
		{
			Parameter t = null;

			//create header row
			sb.Append(Constants.LABEL_REPORT_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PARM_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_DATATYPE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PROMPT);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_HIDDEN);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_MULTIVALUE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_ALLOWNULL);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_ALLOWBLANK);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_VALID_VALUES);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_DEFAULTVALUE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			sb.AppendLine(string.Empty);

			//compare all parameters to template
			foreach (Parameter p in this.ActiveReport.Parameters.Values)
			{
				sb.Append(this.ActiveReport.Name);
				if (this.Template.Parameters.ContainsKey(p.Name))
				{
					t = this.Template.Parameters[p.Name];
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, p.Name);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.DataType == t.DataType) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (t.Prompt == "*" || (p.Prompt.Equals(t.Prompt, StringComparison.CurrentCultureIgnoreCase))) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.Hidden == t.Hidden) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.MultiValue == t.MultiValue) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.Nullable == t.Nullable) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.AllowBlank == t.AllowBlank) ? Constants.LABEL_YES : Constants.LABEL_NO);
					//sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (DataSet.CompareToTemplate(p.DataSet, t.DataSet) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (ValidValues.CompareToTemplate(p.ValidValues, t.ValidValues) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (DefaultValues.CompareToTemplate(p.DefaultValue, t.DefaultValue) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.Equals(t) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendLine(string.Empty);
				}
				else
				{
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, p.Name);
					for (int i = 0; i < 10; i++)
					{
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NA);
					}
					sb.AppendLine(string.Empty);
				}
				t = null;
			}
		}
		private void CreateParamterConformityAllReports(StringBuilder sb)
		{
			bool pass = true;
			bool match = false;
			DataRow dr = null;
			DataTable dt = new DataTable();
			List<string> columnNames = new List<string>();
			List<string> extraColumns = new List<string>();

			//first get list of all paramenter names defined in the parameter dictionary
			foreach (Parameter tp in this.Template.Parameters.Values)
			{
				columnNames.Add(tp.Name);
			}
			columnNames.Sort();
			columnNames.Add("DIVIDER");

			//now find any parameter names not defined in the template
			foreach (ReportWrapper r in this.Reports.Values)
			{
				if (r.Parameters != null)
				{
					foreach (Parameter p in r.Parameters.Values)
					{
						if (string.IsNullOrEmpty(columnNames.Find(delegate (string s) { return s.Equals(p.Name, StringComparison.CurrentCultureIgnoreCase); })))
						{
							if (string.IsNullOrEmpty(extraColumns.Find(delegate(string s) { return s.Equals(p.Name, StringComparison.CurrentCultureIgnoreCase); })))
							{
								extraColumns.Add(p.Name);
							}
						}
					}
				}
			}
			extraColumns.Sort();
			columnNames.AddRange(extraColumns);

			//create data table to contain list of pass/vail values for all parameters across all reports...
			dt.Columns.Add(new DataColumn(Constants.LABEL_REPORT_NAME));
			dt.Columns.Add(new DataColumn(Constants.LABEL_PASSFAIL));
			foreach (string col in columnNames)
			{
				dt.Columns.Add(new DataColumn(col));
			}

			foreach (ReportWrapper r in this.Reports.Values)
			{
				//create a new row for the current report
				dr = dt.NewRow();
				dr[Constants.LABEL_REPORT_NAME] = r.Name;
				pass = true;

				if (r.Parameters != null)
				{
					foreach (Parameter p in r.Parameters.Values)
					{
						match = false;

						//call each paramters Equal() method, passing in corresponding template parameter...
						if (this.Template.Parameters.ContainsKey(p.Name))
						{
							match = p.Equals(this.Template.Parameters[p.Name]);
							if (match)
							{
								dr[p.Name] = Constants.LABEL_YES;
							}
							else
							{
								pass = false;
								dr[p.Name] = Constants.LABEL_NO;
							}
						}
						else
						{
							pass = false;
							dr[p.Name] = Constants.LABEL_NA;
						}
					}
				}
				dr[Constants.LABEL_PASSFAIL] = (pass ? Constants.LABEL_YES : Constants.LABEL_NO);
				dr["DIVIDER"] = "!";

				dt.Rows.Add(dr);
			}

			sb.Append(Constants.LABEL_REPORT_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			foreach (string s in columnNames)
			{
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, s);
			}
			sb.AppendLine(string.Empty);
			foreach (DataRow row in dt.Rows)
			{
				sb.Append(row[Constants.LABEL_REPORT_NAME]);
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, row[Constants.LABEL_PASSFAIL]);

				for (int i = 2; i < dt.Columns.Count; i++)
				{
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, row[dt.Columns[i].ColumnName]);
				}
				sb.AppendLine(string.Empty);
			}
		}
		private void CreateAttributeConformityAllReports(StringBuilder sb)
		{
			//todo: complete this report method
		}
		private void CreateDetailedParameterComparison(StringBuilder sb)
		{
			//todo: complete this report method
		}
		#endregion

		public enum DetailLevel
		{
			ParameterConformityByReport,
			AttributeConformityByReport,
			ParameterConformityAllReports,
			AttributeConformityAllReports,
			DetailedParameterComparison
		}
	}

	public class ReportWrapper
	{
		#region PRIVATE MEMBERS
		private string _name = string.Empty;
		private string _path = string.Empty;
		private Dictionary<string, Parameter> _parameters = null;
		private Dictionary<string, DataSet> _dataSets = null;
		private Report _origReport = null;
		#endregion

		#region PUBLIC PROPERTIES
		public string Name
		{
			get { return this._name; }
		}
		public string Path
		{
			get { return this._path; }
		}
		public Dictionary<string, Parameter> Parameters
		{
			get { return this._parameters; }
		}
		public Dictionary<string, DataSet> DataSets
		{
			get { return this._dataSets; }
		}
		public Report OriginalReport
		{
			get { return this._origReport; }
		}
		public bool IsDirty
		{
			get
			{
				foreach (Parameter p in this._parameters.Values)
				{
					if (p.IsDirty)
					{
						return true;
					}
				}
				return false;
			}
		}
		public bool ReadOnly
		{
			get { return new FileInfo(this._path).IsReadOnly; }
		}
		#endregion

		#region CONSTRUCTOR(S)
		public ReportWrapper(Report report, FileInfo fi)
		{
			this._name = fi.Name;
			this._path = fi.FullName;
			this._origReport = report;
			this._parameters = ReportWrapper.CreateParameterList(report);
			this._dataSets = ReportWrapper.CreateDataSetList(report);
			foreach (Parameter p in this._parameters.Values)
			{
				DataSet vds = p.ValuesDataSet;
				DataSet dds = p.DefaultDataSet;

				if (vds != null)
				{
					this._dataSets[vds.Name] = vds;
				}
				if (dds != null)
				{
					this._dataSets[dds.Name] = dds;
				}
			}
		}
		#endregion

		#region PUBLIC METHODS
		public void FinalizeChanges()
		{
			try
			{
				foreach (Parameter p in this._parameters.Values)
				{
					p.SaveState();
				}
				ReportManager.SaveChanges(this);
			}
			catch (Exception ex)
			{
				//if error, display message and reload file?
			}
		}
		#endregion

		#region PRIVATE METHODS
		public static Dictionary<string, Parameter> CreateParameterList(Report r)
		{
			Dictionary<string, Parameter> parms = null;
			List<ItemsChoiceType37> elements = new List<ItemsChoiceType37>(r.ItemsElementName);

			try
			{
				int parmIdx = elements.IndexOf(ItemsChoiceType37.ReportParameters);
				if (parmIdx > -1)
				{
					parms = new Dictionary<string, Parameter>();

					DataSetType[] dataSets = null;
					int dsIdx = elements.IndexOf(ItemsChoiceType37.DataSets);
					if (dsIdx > -1)
					{
						dataSets = ((DataSetsType)r.Items[dsIdx]).DataSet;
					}
					foreach (ReportParameterType rpt in ((ReportParametersType)r.Items[parmIdx]).ReportParameter)
					{
						parms.Add(rpt.Name, new Parameter(rpt, dataSets));
					}
				}
			}
			catch (IndexOutOfRangeException ex)
			{
				Console.WriteLine(ex.Message);
			}
			return parms;
		}
		public static Dictionary<string, DataSet> CreateDataSetList(Report r)
		{
			Dictionary<string, DataSet> dataSets = new Dictionary<string, DataSet>();
			List<ItemsChoiceType37> elements = new List<ItemsChoiceType37>(r.ItemsElementName);

			try
			{
				int dsIdx = elements.IndexOf(ItemsChoiceType37.DataSets);
				if (dsIdx > -1)
				{
					DataSetType[] dsts = ((DataSetsType)r.Items[dsIdx]).DataSet;
					foreach (DataSetType dst in dsts)
					{
						dataSets.Add(dst.Name, new DataSet(dst));
					}
				}
			}
			catch (IndexOutOfRangeException ex)
			{
				Console.WriteLine(ex.Message);
			}
			return dataSets;
		}
		#endregion
	}

	public class Parameter : IEquatable<Parameter>, IFinalize
	{
		#region PRIVATE MEMBERS
		private string _name								= string.Empty;
		private string _prompt								= string.Empty;
		private bool _hidden								= false;
		private bool _multiValue							= false;
		private bool _nullable								= false;
		private bool _allowBlank							= false;
		private bool _isDirty								= false;
		private ReportWrapper _parent						= null;
		private ReportParameterType _origParm				= null;
		private ReportParameterTypeDataType _dataType		= ReportParameterTypeDataType.String;
		private ValidValues _validValues					= null;
		private DefaultValues _defaultValue					= null;
		#endregion

		#region CONSTRUCTOR(S)
		public Parameter(ReportParameterType parm, DataSetType[] dsts)
		{
			this._name = parm.Name;
			this._origParm = parm;

			for (int i = 0; i < parm.ItemsElementName.Length; i++)
			{
				switch (parm.ItemsElementName[i])
				{
					case ItemsChoiceType33.AllowBlank:
						this._allowBlank = (bool)parm.Items[i];
						break;
					case ItemsChoiceType33.DataType:
						this._dataType = (ReportParameterTypeDataType)parm.Items[i];
						break;
					case ItemsChoiceType33.DefaultValue:
						this._defaultValue = new DefaultValues((DefaultValueType)parm.Items[i], dsts);
						break;
					case ItemsChoiceType33.Hidden:
						this._hidden = (bool)parm.Items[i];
						break;
					case ItemsChoiceType33.MultiValue:
						this._multiValue = (bool)parm.Items[i];
						break;
					case ItemsChoiceType33.Nullable:
						this._nullable = (bool)parm.Items[i];
						break;
					case ItemsChoiceType33.Prompt:
						this._prompt = parm.Items[i].ToString();
						break;
					case ItemsChoiceType33.ValidValues:
						this._validValues = new ValidValues((ValidValuesType)parm.Items[i], dsts);
						break;
				}
			}
		}
		#endregion

		#region PUBLIC PROPERTIES
		public string Name
		{
			get { return _name; }
			set
			{
				this._name = value;
				this._isDirty = true;
			}
		}
		public string Prompt
		{
			get { return _prompt; }
			set
			{
				this._prompt = value;
				this._isDirty = true;
			}
		}
		public bool Hidden
		{
			get { return _hidden; }
			set
			{
				this._hidden = value;
				this._isDirty = true;
			}
		}
		public bool MultiValue
		{
			get { return _multiValue; }
			set
			{
				this._multiValue = value;
				this._isDirty = true;
			}
		}
		public bool AllowBlank
		{
			get { return _allowBlank; }
			set
			{
				this._allowBlank = value;
				this._isDirty = true;
			}
		}
		public bool Nullable
		{
			get { return _nullable; }
			set
			{
				this._nullable = value;
				this._isDirty = true;
			}
		}
		public ReportParameterTypeDataType DataType
		{
			get { return _dataType; }
			set
			{
				this._dataType = value;
				this._isDirty = true;
			}
		}
		public ValidValues ValidValues
		{
			get { return _validValues; }
		}
		public DefaultValues DefaultValue
		{
			get { return _defaultValue; }
		}
		public bool IsDirty
		{
			get
			{
				if (this._isDirty)
				{
					return true;
				}
				else
				{
					return (this._validValues.IsDirty || this._defaultValue.IsDirty);
				}
			}
		}
		public DataSet ValuesDataSet
		{
			get
			{
				if (this._validValues != null && this._validValues.DataSet != null && this._validValues.DataSet.Target != null)
				{
					return this._validValues.DataSet.Target;
				}
				else
				{
					return null;
				}
			}
		}
		public DataSet DefaultDataSet
		{
			get
			{
				if (this._defaultValue != null && this._defaultValue.Values != null && this._defaultValue.Values.DataSet != null && this._defaultValue.Values.DataSet.Target != null)
				{
					return this._defaultValue.Values.DataSet.Target;
				}
				else
				{
					return null;
				}
			}
		}
		#endregion

		#region PUBLIC METHODS
		public bool ReferencesDataSet(string name)
		{
			DataSet dds = this.DefaultDataSet;
			DataSet vds = this.ValuesDataSet;
			if (dds == null && vds == null)
			{
				return false;
			}
			else if ((dds != null && !dds.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) || (vds != null && !vds.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)))
			{
				return true;
			}
			return false;
		}
		public bool ReferencesSproc(string name)
		{
			DataSet dds = this.DefaultDataSet;
			DataSet vds = this.ValuesDataSet;

			if (dds == null && vds == null)
			{
				return false;
			}
			else if ((dds != null && dds.Query != null && dds.Query.CommandText.Equals(name, StringComparison.CurrentCultureIgnoreCase)) || (vds != null && vds.Query != null && vds.Query.CommandText.Equals(name, StringComparison.CurrentCultureIgnoreCase)))
			{
				return true;
			}
			return false;
		}
		#endregion

		#region PRIVATE METHODS
		private object GetRDLValue(ItemsChoiceType33 type)
		{
			for (int i = 0; i < this._origParm.ItemsElementName.Length; i++)
			{
				if (this._origParm.ItemsElementName[i] == type)
				{
					return this._origParm.Items[i];
				}
			}
			return null;
		}
		private void SetRDLValue(ItemsChoiceType33 type, object o)
		{
			for (int i = 0; i < this._origParm.ItemsElementName.Length; i++)
			{
				if (this._origParm.ItemsElementName[i] == type)
				{
					this._origParm.Items[i] = o;
					break;
				}
			}
		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)
		public bool Equals(Parameter other)
		{
			if (other == null)
			{
				return false;
			}
			if (!ValidValues.CompareToTemplate(this.ValidValues, other.ValidValues))
			{
				return false;
			}
			if (!DefaultValues.CompareToTemplate(this.DefaultValue, other.DefaultValue))
			{
				return false;
			}
			return
			(
				this.Name == other.Name &&
				this.AllowBlank == other.AllowBlank &&
				this.DataType == other.DataType &&
				this.Hidden == other.Hidden &&
				this.MultiValue == other.MultiValue &&
				this.Nullable == other.Nullable &&
				(other.Prompt == "*" || this.Prompt.Equals(other.Prompt, StringComparison.CurrentCultureIgnoreCase))
			);
		}
		public void SaveState()
		{
			//TODO: create FinalizeChanges() method...

			//determine what has changed and update any related values in the original report
			if (this._origParm.Name != this._name)
			{
				//find all references to original name in report body and change?

				this._origParm.Name = this._name;
			}

			//this.SetRDLValue(ItemsChoiceType33.Prompt, value);
			//this.SetRDLValue(ItemsChoiceType33.Hidden, value);
			//this.SetRDLValue(ItemsChoiceType33.MultiValue, value);
			//this.SetRDLValue(ItemsChoiceType33.AllowBlank, value);
			//this.SetRDLValue(ItemsChoiceType33.Nullable, value);
			//this.SetRDLValue(ItemsChoiceType33.DataType, value);

			//this.ValidValues
			//this.DefaultValues

		}

		#endregion
	}

	public class ValidValues : IEquatable<ValidValues>
	{
		#region PRIVATE MEMBERS
		private ValueType _vt = ValueType.Undefined;
		private DataSetReference _dsRef = null;
		private CustomList<ParameterValue> _values = null;
		private ValidValuesType _origVVT = null;
		private object[] _origDVT = null;
		#endregion

		#region PUBLIC PROPERTIES
		public DataSetReference DataSet
		{
			get { return this._dsRef; }
		}
		public CustomList<ParameterValue> Values
		{
			get { return this._values; }
		}
		public bool IsDirty
		{
			get
			{
				if (this._dsRef != null && this._dsRef.IsDirty)
				{
					return true;
				}
				else if (this._values != null)
				{
					if (this._values.IsDirty)
					{
						return true;
					}
				}
				return false;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		//used by ValidValues object
		public ValidValues(ValidValuesType vvt, DataSetType[] dsts)
		{
			this._origVVT = vvt;
			this._vt = ValueType.ValidValues;

			for (int i = 0; i < this._origVVT.Items.Length; i++)
			{
				if (this._origVVT.Items[i] is DataSetReferenceType)
				{
					this._dsRef = new DataSetReference((DataSetReferenceType)this._origVVT.Items[i], dsts);
				}
				else if (this._origVVT.Items[i] is ParameterValuesType)
				{
					List<ParameterValue> tmp = new List<ParameterValue>();
					foreach (ParameterValueType pvt in ((ParameterValuesType)this._origVVT.Items[i]).ParameterValue)
					{
						tmp.Add(new ParameterValue(pvt));
					}
					this._values = new CustomList<ParameterValue>(tmp);
				}
			}
		}
		//used by the DefaultValues object (has different schema than ValidValues object)
		public ValidValues(object[] items, DataSetType[] dsts)
		{
			this._origDVT = items;
			this._vt = ValueType.DefaultValues;

			for (int i = 0; i < this._origDVT.Length; i++)
			{
				if (this._origDVT[i] is DataSetReferenceType)
				{
					this._dsRef = new DataSetReference((DataSetReferenceType)this._origDVT[i], dsts);
				}
				else if (this._origDVT[i] is ValuesType)
				{
					this._values = new CustomList<ParameterValue>();
					for (int j = 0; j < this._origDVT.Length; j++)
					{
						this.Values.Add(new ParameterValue((ValuesType)this._origDVT[i], j));
					}
				}
			}
		}
		#endregion

		#region PUBLIC METHODS
		public void ChangeDataSetReference(DataSet ds)
		{
			switch (this._vt)
			{
				case ValueType.ValidValues:
					if (this._origVVT != null)
					{
						for (int i = 0; i < this._origVVT.Items.Length; i++)
						{
							if (this._origVVT.Items[i] is DataSetReferenceType)
							{
								//change value of original

								//set dirty flag

								return;
							}
						}
						//if this get's to here, there was no previously existing DataSet
					}
					break;
				case ValueType.DefaultValues:
					if (this._origDVT != null)
					{

					}
					break;
			}
		}
		public void CreateNewValuesCollection()
		{

		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)
		public static bool CompareToTemplate(ValidValues parmValue, ValidValues templateValue)
		{
			if (parmValue == null && templateValue != null)
			{
				return false;
			}
			else if (parmValue != null && parmValue.Equals(templateValue))
			{
				return true;
			}
			else if (parmValue == null && templateValue == null)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		public static bool AcceptsAnyValue(ValidValues vv)
		{
			foreach (ParameterValue pv in vv.Values)
			{
				if (pv != null && (pv.Value == "*" || pv.Label == "*"))
				{
					return true;
				}
			}
			return false;
		}
		public bool Equals(ValidValues other)
		{
			if (other == null)
			{
				return false;
			}
			if (this.Values == null && other.Values == null)
			{
				return true;
			}
			if (this.Values != null && other.Values == null)
			{
				return false;
			}
			if (other.Values != null)
			{
				if (ValidValues.AcceptsAnyValue(other))
				{
					return true;
				}
				else if (this.Values == null)
				{
					return false;
				}
				else
				{
					if (this.Values.Count != other.Values.Count)
					{
						return false;
					}
					else if (this.Values.Count == 1 && this.Values[0] != null)
					{
						return this.Values[0].Equals(other.Values[0]);
					}
					else
					{
						bool found = false;
						foreach (ParameterValue pv in this.Values)
						{
							foreach (ParameterValue ov in other.Values)
							{
								if (pv != null && pv.Equals(ov))
								{
									found = true;
									break;
								}
							}
							if (!found)
							{
								return false;
							}
							else
							{
								found = false;
							}
						}
						foreach (ParameterValue ov in other.Values)
						{
							foreach (ParameterValue pv in other.Values)
							{
								if (ov != null && ov.Equals(pv))
								{
									found = true;
									break;
								}
							}
							if (!found)
							{
								return false;
							}
							else
							{
								found = false;
							}
						}
					}
				}
			}
			if (this.DataSet == null && other.DataSet != null)
			{
				return false;
			}
			if (this.DataSet != null && !this.DataSet.Equals(other.DataSet))
			{
				return false;
			}
			return true;
		}
		#endregion

		private enum ValueType
		{
			Undefined,
			DefaultValues,
			ValidValues
		}
	}

	public class DefaultValues : IEquatable<DefaultValues>
	{
		#region PRIVATE MEMBER(S)
		private ValidValues _values = null;
		#endregion

		#region PUBLIC PROPERTIES
		public ValidValues Values
		{
			get { return this._values; }
		}
		public bool IsDirty
		{
			get
			{
				if (this._values != null)
				{
					return this._values.IsDirty;
				}
				return false;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public DefaultValues(DefaultValueType dvt, DataSetType[] dsts)
		{
			this._values = new ValidValues(dvt.Items, dsts);
		}
		#endregion

		#region PUBLIC METHOD(S)
		public static bool CompareToTemplate(DefaultValues parmDefault, DefaultValues templateDefault)
		{
			if (parmDefault == null && templateDefault != null)
			{
				return false;
			}
			else if (parmDefault != null && parmDefault.Equals(templateDefault))
			{
				return true;
			}
			else if (parmDefault == null && templateDefault == null)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)
		public bool Equals(DefaultValues other)
		{
			if (other == null)
			{
				return false;
			}
			return this.Values.Equals(other.Values);
		}
		#endregion
	}

	public class DataSetReference : IEquatable<DataSetReference>
	{
		#region PRIVATE MEMBER(S)
		private string _name = string.Empty;
		private string _label = string.Empty;
		private string _value = string.Empty;
		private DataSet _target = null;
		private bool _isDirty = false;
		private DataSetReferenceType _origObj = null;
		#endregion

		#region PUBLIC PROPERTIES
		public string Name
		{
			get { return this._name; }
			set
			{
				this.SetRDLValue(ItemsChoiceType31.DataSetName, value);
			}
		}
		public string LabelField
		{
			get { return this._label; }
			set
			{
				this.SetRDLValue(ItemsChoiceType31.LabelField, value);
			}
		}
		public string ValueField
		{
			get { return this._value; }
			set
			{
				this.SetRDLValue(ItemsChoiceType31.ValueField, value);
			}
		}
		public DataSet Target
		{
			get { return this._target; }
		}
		public bool IsDirty
		{
			get
			{
				if (this._isDirty)
				{
					return true;
				}
				else
				{
					return (this._target != null && this._target.IsDirty);
				}
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public DataSetReference(DataSetReferenceType dsrt, DataSetType[] dsts)
		{
			this._origObj = dsrt;

			for (int i = 0; i < dsrt.ItemsElementName.Length; i++)
			{
				switch (dsrt.ItemsElementName[i])
				{
					case ItemsChoiceType31.DataSetName:
						this._name = dsrt.Items[i].ToString();
						foreach (DataSetType dst in dsts)
						{
							if (dst.Name.Equals(this._name, StringComparison.CurrentCultureIgnoreCase))
							{
								this._target = new DataSet(dst);
								break;
							}
						}
						break;
					case ItemsChoiceType31.LabelField:
						this._label = dsrt.Items[i].ToString();
						break;
					case ItemsChoiceType31.ValueField:
						this._value = dsrt.Items[i].ToString();
						break;
				}
			}
		}
		#endregion

		#region PRIVATE METHODS
		private void SetRDLValue(ItemsChoiceType31 type, object value)
		{
			List<ItemsChoiceType31> elements = new List<ItemsChoiceType31>(this._origObj.ItemsElementName);
			int idx = elements.IndexOf(type);

			if (idx > -1)
			{
				this._origObj.Items[idx] = value;
				this._isDirty = true;
			}
		}
		#endregion

		#region INTERFACE IMPLMENTATION(S)
		public bool Equals(DataSetReference other)
		{
			if (other == null)
			{
				return false;
			}
			else if (this.Target == null && other.Target == null)
			{
				return (this.Name.Equals(other.Name, StringComparison.CurrentCultureIgnoreCase));
			}
			else if (this.Target != null && this.Target.Equals(other.Target))
			{
				return true;
			}
			return false;
		}
		#endregion
	}

	public class ParameterValue : IEquatable<ParameterValue>, IDirty
	{
		#region PRIVATE MEMBERT(S)
		private string _label = string.Empty;
		private string _value = string.Empty;
		private bool _isDirty = false;
		private ParameterValueType _origPVT = null;
		private object _origVT = null;
		#endregion

		#region PUBLIC PROPERTIES
		public string Label
		{
			get { return this._label; }
			set
			{
				this.SetRDLValue(ItemsChoiceType32.Label, value);
			}
		}
		public string Value
		{
			get { return this._value; }
			set
			{
				this.SetRDLValue(ItemsChoiceType32.Value, value);
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public ParameterValue(ValuesType vt, int idx)
		{
			this._origVT = vt;

		}
		public ParameterValue(ParameterValueType pvt)
		{
			this._origPVT = pvt;
			for (int i = 0; i < pvt.ItemsElementName.Length; i++)
			{
				switch (pvt.ItemsElementName[i])
				{
					case ItemsChoiceType32.Label:
						this.Label = pvt.Items[i].ToString();
						break;
					case ItemsChoiceType32.Value:
						this.Value = pvt.Items[i].ToString();
						break;
				}
			}
		}
		#endregion

		#region PRIVATE METHODS
		private void SetRDLValue(ItemsChoiceType32 type, object value)
		{
			if (this._origPVT != null && this._origPVT.ItemsElementName != null)
			{
				List<ItemsChoiceType32> elements = new List<ItemsChoiceType32>(this._origPVT.ItemsElementName);
				int idx = elements.IndexOf(type);

				if (idx > -1)
				{
					this._origPVT.Items[idx] = value;
					this._isDirty = true;
				}
			}
		}
		#endregion

		#region	INTERFACE IMPLEMENTATION(S)
		public bool Equals(ParameterValue other)
		{
			return (other == null) ? false : (this.Label.Equals(other.Label, StringComparison.CurrentCultureIgnoreCase) && 
				this.Value.Equals(other.Value, StringComparison.CurrentCultureIgnoreCase));
		}
		bool IDirty.IsDirty
		{
			get { return this._isDirty; }
		}

		#endregion
	}

	public class DataSet : IEquatable<DataSet>
	{
		#region PRIVATE MEMBERS
		private string _name = string.Empty;
		private Query _query = null;
		private CustomList<Field> _fields = null;
		private DataSetType _origParm = null;
		private bool _isDirty = false;
		#endregion

		#region PUBLIC MEMBERS
		public string Name
		{
			get { return this._name; }
			set
			{
				this._isDirty = true;
				this._origParm.Name = value;
			}
		}
		public Query Query
		{
			get { return this._query; }
		}
		public CustomList<Field> Fields
		{
			get { return this._fields; }
		}
		public bool IsDirty
		{
			get
			{
				if (this._isDirty)
				{
					return true;
				}
				else if (this._query != null && this._query.IsDirty)
				{
					return true;
				}
				else
				{
					if (Fields.IsDirty)
					{
						return true;
					}
				}
				return false;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public DataSet(DataSetType dst)
		{
			this._name = dst.Name;
			this._origParm = dst;

			for (int i = 0; i < dst.Items.Length; i++)
			{
				if (dst.Items[i] is QueryType)
				{
					this._query = new Query((QueryType)dst.Items[i]);
				}
				else if (dst.Items[i] is FieldsType)
				{
					this._fields = new CustomList<Field>();
					foreach (FieldType ft in ((FieldsType)dst.Items[i]).Field)
					{
						this._fields.Add(new Field(ft));
					}
				}
			}
		}
		#endregion

		public static bool CompareToTemplate(DataSet parmDataSet, DataSet templateDataSet)
		{
			if (parmDataSet == null && templateDataSet != null)
			{
				return false;
			}
			else if (parmDataSet != null && parmDataSet.Equals(templateDataSet))
			{
				return true;
			}
			else if (parmDataSet == null && templateDataSet == null)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		public object GetRDLValue()
		{
			return null;
		}
		public void SetRDLValue()
		{

		}

		#region INTERFACE IMPLEMENTATION(S)
		public bool Equals(DataSet other)
		{
			if (other == null)
			{
				return false;
			}

			#region COMMENTED-OUT CODE
			//if ((this.Fields == null && other.Fields != null) || (this.Fields != null && other.Fields == null))
			//{
			//    return false;
			//}
			//if (this.Fields != null)
			//{
			//    if (this.Fields.Count != other.Fields.Count)
			//    {
			//        return false;
			//    }
			//    else
			//    {
			//        bool found = false;
			//        foreach (Field f in this.Fields)
			//        {
			//            foreach (Field o in other.Fields)
			//            {
			//                if (f.Equals(o))
			//                {
			//                    found = true;
			//                    break;
			//                }
			//            }
			//            if (!found)
			//            {
			//                return false;
			//            }
			//            else
			//            {
			//                found = false;
			//            }
			//        }
			//        foreach (Field o in other.Fields)
			//        {
			//            foreach (Field f in this.Fields)
			//            {
			//                if (o.Equals(f))
			//                {
			//                    found = true;
			//                    break;
			//                }
			//            }
			//            if (!found)
			//            {
			//                return false;
			//            }
			//            else
			//            {
			//                found = false;
			//            }
			//        }
			//    }
			//}
			#endregion

			return ReportMaintenanceUtility.Query.CompareToTemplate(this.Query, other.Query);
		}
		#endregion
	}

	public class Query : IEquatable<Query>
	{
		#region PRIVATE MEMBERS
		private bool _isDirty = false;
		private string _cmdText = string.Empty;
		private string _dsName = string.Empty;
		private QueryTypeCommandType _qtct = QueryTypeCommandType.StoredProcedure;
		private CustomList<QueryParameter> _parms = null;
		private QueryType _origObj = null;
		#endregion

		#region PUBLIC PROPERTIES
		public bool IsDirty
		{
			get
			{
				if (this._isDirty)
				{
					return true;
				}
				else if (this._parms != null)
				{
					return this._parms.IsDirty;
				}
				else
				{
					return false;
				}
			}
		}
		public string CommandText
		{
			get { return this._cmdText; }
			set
			{
				this.SetRDLValue(ItemsChoiceType2.CommandText, value);
			}
		}
		public string DataSourceName
		{
			get { return this._dsName; }
			set
			{
				this.SetRDLValue(ItemsChoiceType2.DataSourceName, value);
			}
		}
		public QueryTypeCommandType CommandType
		{
			get { return this._qtct; }
			set
			{
				this.SetRDLValue(ItemsChoiceType2.CommandType, value);
			}
		}
		public CustomList<QueryParameter> Parameters
		{
			get { return this._parms; }
		}
		#endregion

		#region CONSTRUCTOR(S)
		public Query(QueryType qt)
		{
			this._origObj = qt;
			for (int i = 0; i < qt.ItemsElementName.Length; i++)
			{
				switch (qt.ItemsElementName[i])
				{
					case ItemsChoiceType2.CommandText:
						this._cmdText = qt.Items[i].ToString();
						break;
					case ItemsChoiceType2.CommandType:
						this._qtct = (QueryTypeCommandType)qt.Items[i];
						break;
					case ItemsChoiceType2.DataSourceName:
						this._dsName = qt.Items[i].ToString();
						break;
					case ItemsChoiceType2.QueryParameters:
						this._parms = new CustomList<QueryParameter>();
						foreach (QueryParameterType qpt in ((QueryParametersType)qt.Items[i]).QueryParameter)
						{
							this._parms.Add(new QueryParameter(qpt));
						}
						break;
				}
			}
		}
		#endregion

		#region PUBLIC METHODS
		public static bool CompareToTemplate(Query parmQuery, Query templateQuery)
		{
			if (parmQuery == null && templateQuery == null)
			{
				return true;
			}
			else if (parmQuery == null && templateQuery != null)
			{
				return false;
			}
			else if (parmQuery != null && parmQuery.Equals(templateQuery))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		#endregion

		#region PRIVATE METHODS
		private void SetRDLValue(ItemsChoiceType2 type, object value)
		{
			if (this._origObj != null)
			{
				List<ItemsChoiceType2> elements = new List<ItemsChoiceType2>(this._origObj.ItemsElementName);
				int idx = elements.IndexOf(type);
				if (idx > -1)
				{
					this._origObj.Items[idx] = value;
					this._isDirty = true;
				}
			}
		}
		#endregion

		#region	INTERFACE IMPLEMENATION(S)
		public bool Equals(Query other)
		{
			if (other == null)
			{
				return false;
			}
			if (!((this.CommandText.Equals(other.CommandText, StringComparison.CurrentCultureIgnoreCase) &&
				(this.CommandType == other.CommandType) &&
				(this.DataSourceName.Equals(other.DataSourceName, StringComparison.CurrentCultureIgnoreCase)))))
			{
				return false;
			}

			#region COMMENTED-OUT CODE
			//if ((this.Parameters == null && other.Parameters != null) || this.Parameters != null && other.Parameters == null)
			//{
			//    return false;
			//}
			//if (this.Parameters != null)
			//{
			//    if (this.Parameters.Count != other.Parameters.Count)
			//    {
			//        return false;
			//    }
			//    else
			//    {
			//        bool found = false;
			//        foreach (QueryParameter p in this.Parameters)
			//        {
			//            foreach (QueryParameter o in other.Parameters)
			//            {
			//                if (p != null && p.Equals(o))
			//                {
			//                    found = true;
			//                    break;
			//                }
			//            }
			//            if (!found)
			//            {
			//                return false;
			//            }
			//            else
			//            {
			//                found = false;
			//            }
			//        }
			//        found = false;
			//        foreach (QueryParameter o in other.Parameters)
			//        {
			//            foreach (QueryParameter p in other.Parameters)
			//            {
			//                if (o != null && o.Equals(p))
			//                {
			//                    found = true;
			//                    break;
			//                }
			//            }
			//            if (!found)
			//            {
			//                return false;
			//            }
			//            else
			//            {
			//                found = false;
			//            }
			//        }
			//    }
			//}
			#endregion

			return true;
		}
		#endregion
	}

	public class QueryParameter : IEquatable<QueryParameter>, IDirty
	{
		#region PRIVATE MEMBERS
		private string _name = string.Empty;
		private string _value = string.Empty;
		private QueryParameterType _origObj = null;
		#endregion

		#region PUBLIC PROPERTIES
		public string Name
		{
			get { return this._name; }
		}
		public string Value
		{
			get { return this._value; }
		}
		#endregion

		#region CONSTRUCTOR(S)
		public QueryParameter(QueryParameterType qpt)
		{
			this._name = qpt.Name;
			this._origObj = qpt;
			if (qpt.Items.Length > 0 && qpt.Items[0] != null)
			{
				this._value = qpt.Items[0].ToString();
			}
		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)
		public bool Equals(QueryParameter other)
		{
			if (other == null)
			{
				return false;
			}
			return (this.Value.Equals(other.Value, StringComparison.CurrentCultureIgnoreCase) &&
				this.Name.Equals(other.Name, StringComparison.CurrentCultureIgnoreCase));
		}
		#endregion

		#region IDirty Members

		public bool IsDirty
		{
			//todo: replace once ability to change object values is added
			get { return false; }
		}

		#endregion
	}

	public class Field : IEquatable<Field>, IDirty
	{
		#region PRIVATE MEMBER(S)
		private bool _isDirty = false;
		private string _name = string.Empty;
		private string _type = string.Empty;
		private FieldType _origObj = null;
		#endregion

		#region PUBLIC PROPERTIES
		public string Name
		{
			get { return this._name; }
			set
			{
				this._origObj.Name = value;
				this._isDirty = true;
			}
		}
		public string Type
		{
			get { return this._type; }
			set
			{
				if (this._origObj != null)
				{
					List<ItemsChoiceType1> elements = new List<ItemsChoiceType1>(this._origObj.ItemsElementName);
					int idx = elements.IndexOf(ItemsChoiceType1.Item);

					if (idx > -1)
					{
						try
						{
							((XmlElement)this._origObj.Items[idx]).InnerText = value;
							this._isDirty = true;
						}
						catch (Exception ex)
						{
							Debug.Print(ex.Message);
						}
					}
				}
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public Field(FieldType ft)
		{
			this._origObj = ft;
			this._name = ft.Name;
			for (int i = 0; i < ft.ItemsElementName.Length; i++)
			{
				if (ft.ItemsElementName[i] == ItemsChoiceType1.Item)
				{
					try
					{
						this.Type = ((XmlElement)ft.Items[i]).InnerText;
					}
					catch (InvalidCastException ex)
					{
						Debug.Print(ex.Message);
					}
					break;
				}
			}
		}
		#endregion

		#region INTERFACE IMPLEMENTATION(S)
		public bool Equals(Field other)
		{
			if (other == null)
			{
				return false;
			}
			return (this.Name.Equals(other.Name, StringComparison.CurrentCultureIgnoreCase) &&
				this.Type.Equals(other.Type, StringComparison.CurrentCultureIgnoreCase));
		}
		bool IDirty.IsDirty
		{
			get
			{
				return this._isDirty;
			}
		}

		#endregion
	}

	public static class Constants
	{
		public const string FORMAT_REPORT_ITEM = "\t{0}";
		public const string LABEL_REPORT_NAME = "ReportName";
		public const string LABEL_PARM_NAME = "ParmName";
		public const string LABEL_DATATYPE = "DataType";
		public const string LABEL_PROMPT = "Prompt";
		public const string LABEL_HIDDEN = "Hidden";
		public const string LABEL_INTERNAL = "Internal";
		public const string LABEL_MULTIVALUE = "MultiValue";
		public const string LABEL_ALLOWNULL = "AllowNull";
		public const string LABEL_ALLOWBLANK = "AllowBlank";
		public const string LABEL_VALUES_DATASET = "ValuesDataSet";
		public const string LABEL_DEFAULT_DATASET = "DefaultDataSet";
		public const string LABEL_VALID_VALUES = "ValidValues";
		public const string LABEL_DEFAULTVALUE = "DefaultValue";
		public const string LABEL_PASSFAIL = "PassFail";
		public const string LABEL_LABELFIELD = "LabelField";
		public const string LABEL_VALUEFIELD = "ValueField";
		public const string LABEL_VALUES = "Values";
		public const string LABEL_COMMANDTEXT = "CommandText";
		public const string LABEL_COMMANDTYPE = "CommandType";
		public const string LABEL_DATASOURCENAME = "DataSourceName";
		public const string LABEL_QUERYPARAMETERS = "QueryParameters";
		public const string LABEL_NA = "N/A";
		public const string LABEL_YES = "Y";
		public const string LABEL_NO = "N";
		public const string PROPERTY_NAME = "Name";
		public const string PROPERTY_LABEL = "Label";
		public const string PROPERTY_VALUE = "Value";

	}

	public interface IDirty
	{
		bool IsDirty { get; }
	}

	public interface IFinalize
	{
		void SaveState();
	}

	public class CustomList<T> : Collection<T> where T : IDirty
	{
		#region PRIVATE MEMBERS
		private bool _isDirty = false;
		#endregion

		#region PUBLIC PROPERTIES
		public bool IsDirty
		{
			get
			{
				if (this._isDirty)
				{
					return true;
				}
				else
				{
					foreach (T t in this.Items)
					{
						if (t.IsDirty)
						{
							return true;
						}
					}
				}
				return false;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public CustomList() : this(new List<T>()) { }
		public CustomList(List<T> list) : base(list) { }
		#endregion

		#region PUBLIC METHODS
		public void AddRange(IEnumerable<T> collection)
		{
			((List<T>)this.Items).AddRange(collection);
		}
		public T Find(Predicate<T> match)
		{
			return ((List<T>)this.Items).Find(match);
		}
		public IEnumerable<T> FindAll(Predicate<T> match)
		{
			return ((List<T>)this.Items).FindAll(match);
		}
		public void Sort()
		{
			((List<T>)this.Items).Sort();
		}
		public void Sort(Comparison<T> comparison)
		{
			((List<T>)this.Items).Sort(comparison);
		}
		public void Sort(IComparer<T> comparer)
		{
			((List<T>)this.Items).Sort(comparer);
		}
		public void Sort(int index, int count, IComparer<T> comparer)
		{
			((List<T>)this.Items).Sort(index, count, comparer);
		}
		public T[] ToArray()
		{
			return ((List<T>)this.Items).ToArray();
		}
		#endregion

		#region PROTECTED OVERRIDE METHODS
		protected override void InsertItem(int index, T newItem)
		{
			base.InsertItem(index, newItem);
			this._isDirty = true;
		}
		protected override void SetItem(int index, T newItem)
		{
			base.SetItem(index, newItem);
			this._isDirty = true;
		}
		protected override void RemoveItem(int index)
		{
			base.RemoveItem(index);
			this._isDirty = true;
		}
		protected override void ClearItems()
		{
			base.ClearItems();
			this._isDirty = true;
		}
	}
}